package com.logica.tutorial.model;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "Person")
public class Person implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = -368088974024499638L;

	@Id
	// @PrimaryKeyJoinColumn
	private Long personId;
	@Enumerated(EnumType.STRING)
	@Column(nullable = false) 
	private Sex sex = Sex.NA;
	private String firstName;
	private String lastName;

	@JoinColumn(name = "address", referencedColumnName = "addressId")
	@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE }, fetch = FetchType.LAZY)
	private Address address;

	@JoinColumn(name = "father", referencedColumnName = "personId")
	@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.REFRESH }, fetch = FetchType.LAZY)
	private Person father;

	@JoinColumn(name = "mother", referencedColumnName = "personId")
	@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.REFRESH }, fetch = FetchType.LAZY)
	private Person mother;

	@OneToMany(cascade = { CascadeType.PERSIST, CascadeType.REFRESH }, fetch = FetchType.LAZY, mappedBy = "father")
	private Collection<Person> fatherChildren;
	@OneToMany(cascade = { CascadeType.PERSIST, CascadeType.REFRESH }, fetch = FetchType.LAZY, mappedBy = "mother")
	private Collection<Person> motherChildren;

	@ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.REFRESH }, fetch = FetchType.LAZY)
	@JoinTable(name = "Friends", joinColumns = @JoinColumn(name = "personId", referencedColumnName = "personId"), 
		inverseJoinColumns = @JoinColumn(name = "friendId", referencedColumnName = "personId"))
	private Collection<Person> friends;
	
	@ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.REFRESH }, fetch = FetchType.LAZY)
	@JoinTable(name = "Friends", joinColumns = @JoinColumn(name = "friendId", referencedColumnName = "personId"), 
		inverseJoinColumns = @JoinColumn(name = "personId", referencedColumnName = "personId"))
	private Collection<Person> friendOf;

	public Long getId() {
		return personId;
	}
	
	public void setId(Long id) {
		this.personId = id;
	}

	public Sex getSex() {
		return sex;
	}

	public void setSex(Sex sex) {
		this.sex = sex;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public Address getAddress() {
		if(address == null) {
			address = new Address();
		}
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	public Person getFather() {
		return father;
	}

	public void setFather(Person person) {
		this.father = person;
	}

	public Person getMother() {
		return mother;
	}

	public void setMother(Person mother) {
		this.mother = mother;
	}

	public Collection<Person> getChildren() {		
		switch (getSex()) {
		case MALE:
			return getFatherChildren();
		case FEMALE:
			return getMotherChildren();		
		default:
			return new HashSet<Person>();
		}			
	}

	private Collection<Person> getFatherChildren() {
		if (fatherChildren == null) {
		    fatherChildren = new HashSet<Person>();
		  }
		return fatherChildren;
	}

	private Collection<Person> getMotherChildren() {
		if (motherChildren == null) {
		    motherChildren = new HashSet<Person>();
		  }		
		return motherChildren;
	}

	public void addChild(Person person) {
		switch (getSex()) {
		case MALE:
			person.setFather(this);
			getChildren().add(person);
			break;
		case FEMALE:
			person.setMother(this);
			getChildren().add(person);
			break;
		default:
			break;
		}	
	}

	public void setChildren(Collection<Person> children) {
		Iterator<Person> it = children.iterator();
		switch (getSex()) {
		case MALE:
			while(it.hasNext()) {
				addChild(it.next());
			}
			break;
		case FEMALE:
			while(it.hasNext()) {
				addChild(it.next());
			}
			break;
		default:
			break;
		}
	}

	public void addFriend(Person person) {
		getFriends().add(person);
	}

	public void setFriends(Collection<Person> friends) {
		Iterator<Person> it = friends.iterator();
		while(it.hasNext()) {
			addFriend(it.next());
		}
	}

	public Collection<Person> getFriends() {
		if (friends == null) {
		    friends = new HashSet<Person>();
		}
		return friends;
	}
	
	public void addFriendOf(Person person) {
		getFriendOf().add(person);
	}
	
	public void setFriendOf(Collection<Person> friendOf) {
		Iterator<Person> it = friendOf.iterator();
		while(it.hasNext()) {
			addFriendOf(it.next());
		}
	}
	
	public Collection<Person> getFriendOf() {
		if (friendOf == null) {
			friendOf = new HashSet<Person>();
		}
		return friendOf;
	}
	
	@Override
	public String toString() {
		return getLastName() + ", " + getFirstName() + ", " + getId();
	}
//	@Override
//	public int hashCode() {
//        return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers
//            // if deriving: appendSuper(super.hashCode()).
//            append(getId()).
//            append(getFirstName()).
//            append(getLastName()).
//            toHashCode();
//    }
//	@Override
//	public boolean equals(Object obj) {
//		if (obj == null)
//            return false;
//		if (obj == this)
//            return true;
//		if (obj.getClass() != getClass())
//            return false;
//		
//		Person rhs = (Person) obj;
//        return new EqualsBuilder().
//        	append(getId(), rhs.getId()).
//            append(getFirstName(), rhs.getFirstName()).
//            append(getLastName(), rhs.getLastName()).
//            isEquals();
//	}
}